//! 时钟事件管理

use alloc::vec::Vec;
use core::ops::DivAssign;

use crate::{
    bsp::{
        __clock_get_set_next_event, __clock_get_set_state_stoped, __clock_max_delta,
        __clock_min_delta, __clock_rate,
    },
    irq::irqflags::{local_irq_restore, local_irq_save},
    processor::{nr_cpus, this_processor_id},
    time::{
        NSEC_PER_SEC, clocksource::clocks_calc_mult_shift, hrtimer::hrtimer_interrupt,
        timekeeping::ktime_get,
    },
};

// 时钟事件结构体
#[derive(Clone, Copy)]
#[repr(align(64))]
struct ClockEvent {
    pub(crate) event_handler: fn(),
    set_next_event: fn(u64) -> bool,
    set_state_stoped: fn(),
    min_delta_ns: u64,
    max_delta_ns: u64,
    min_delta_ticks: u64,
    max_delta_ticks: u64,
    mult: u32,
    shift: u32,
    cpu: usize,
}

#[inline(always)]
fn dummy_event_handler() {}
#[inline(always)]
fn dummy_set_next_event(_: u64) -> bool {
    false
}
#[inline(always)]
fn dummy_set_state_stoped() {}

impl ClockEvent {
    const fn new() -> Self {
        Self {
            event_handler: dummy_event_handler,
            set_next_event: dummy_set_next_event,
            set_state_stoped: dummy_set_state_stoped,
            min_delta_ns: 1,
            max_delta_ns: 1,
            min_delta_ticks: 1,
            max_delta_ticks: 1,
            mult: 1,
            shift: 1,
            cpu: 0,
        }
    }

    fn program_min_delta(&mut self) -> bool {
        for _ in 0..10 {
            let delta = self.min_delta_ns;
            let tick = (delta * u64::from(self.mult)) >> self.shift;
            if (self.set_next_event)(tick) {
                return true;
            }
        }
        false
    }

    fn program_event(&mut self, expires: u64, force: bool) -> bool {
        let now = ktime_get();
        if expires <= now {
            if force {
                return self.program_min_delta();
            }
            return false;
        }

        let mut delta = expires - now;
        delta = delta.min(self.max_delta_ns);
        delta = delta.max(self.min_delta_ns);

        let tick = (delta * u64::from(self.mult)) >> self.shift;
        let rc = (self.set_next_event)(tick);
        if !rc && force { self.program_min_delta() } else { rc }
    }

    fn cev_delta2ns(&mut self, latch: u64, ismax: bool) -> u64 {
        let mut clc = latch << self.shift;
        assert!(self.mult != 0);
        let rnd = self.mult - 1;

        if (clc >> self.shift) != latch {
            clc = u64::MAX;
        }

        if ((u64::MAX - clc) > u64::from(rnd))
            && (!ismax || u64::from(self.mult) <= (1 << u64::from(self.shift)))
        {
            clc += u64::from(rnd);
        }

        clc.div_assign(u64::from(self.mult));
        if clc > 1000 { clc } else { 1000 }
    }

    fn config(&mut self, freq: u64) {
        let mut sec = self.max_delta_ticks;
        sec.div_assign(freq);
        if sec == 0 {
            sec = 1;
        } else if sec > 600 && self.max_delta_ticks > (u64::from(u32::MAX)) {
            sec = 600;
        }

        clocks_calc_mult_shift(
            &mut (self.mult),
            &mut (self.shift),
            NSEC_PER_SEC as u32,
            freq as u32,
            sec as u32,
        );
        self.min_delta_ns = self.cev_delta2ns(self.min_delta_ticks, false);
        self.max_delta_ns = self.cev_delta2ns(self.max_delta_ticks, true);
    }

    fn config_and_register(&mut self, freq: u64, min_tick: u64, max_tick: u64) {
        self.min_delta_ticks = min_tick;
        self.max_delta_ticks = max_tick;
        self.config(freq);
    }

    #[inline(always)]
    fn clockevent_handler(&self) {
        debug_assert_eq!(self.cpu, this_processor_id());
        (self.event_handler)();
    }
}

static mut CLOCKEVENT_DEV: Vec<ClockEvent> = Vec::new();

#[allow(static_mut_refs)]
pub(super) fn clockevent_init() {
    unsafe {
        for _ in 0..nr_cpus() {
            CLOCKEVENT_DEV.push(ClockEvent::new());
        }
    }
    clockevent_init_cpu();
}

pub(super) fn clockevent_init_cpu() {
    let cd = unsafe { &mut CLOCKEVENT_DEV[this_processor_id()] };
    cd.event_handler = hrtimer_interrupt;
    cd.set_next_event = unsafe { __clock_get_set_next_event() };
    cd.set_state_stoped = unsafe { __clock_get_set_state_stoped() };
    cd.cpu = this_processor_id();
    clockevent_shutdown();
    cd.config_and_register(
        unsafe { __clock_rate() as u64 },
        unsafe { __clock_min_delta() },
        unsafe { __clock_max_delta() },
    );
}

#[inline(always)]
pub(super) fn nsec_program_event(expires: u64, force: bool) -> bool {
    let flags = local_irq_save();
    let cd = unsafe { &mut CLOCKEVENT_DEV[this_processor_id()] };
    let res = cd.program_event(expires, force);
    local_irq_restore(flags);
    res
}

#[allow(unused)]
#[inline(always)]
pub(crate) fn clockevent_handler() {
    let cd = unsafe { &CLOCKEVENT_DEV[this_processor_id()] };
    cd.clockevent_handler();
}

/// 停止当前处理器时钟事件
pub fn clockevent_shutdown() {
    let flags = local_irq_save();
    let cd = unsafe { &CLOCKEVENT_DEV[this_processor_id()] };
    (cd.set_state_stoped)();
    local_irq_restore(flags);
}
